package com.eth.util;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.base.exception.SystemException;
import com.base.support.LogKit;
import com.base.util.JSONUtils;
import com.base.util.StringUtil;
import com.eth.model.EthChain;
import com.eth.model.EthChainType;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.*;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.concurrent.ExecutionException;

import static org.web3j.tx.Transfer.GAS_LIMIT;

/**
 * web3j eth接口工具
 */
public class Web3jUtil {
    private static final String INFURA_URL = "https://mainnet.infura.io/sYelaYIPKvXG5mhCNhy6";
    private static final String PRIVATE_KEY = "27d7207c86a17390a4316b23c639efccfe4e524af91cf31b78c4da5debfa3303";
    private static final String BLOCK_HASH = "0x0000000000000000000000000000000000000000000000000000000000000000";
    private static Web3j web3j;
    private static Credentials credentials;
    private static String address;
    static {
        init();
    }

    private static void init(){
        web3j = Web3j.build(new HttpService(INFURA_URL));
        credentials = Credentials.create(PRIVATE_KEY);
        address = credentials.getAddress();
    }

    /**
     * 查询账户余额
     */
    public static BigDecimal getBalance(){
        try {
            EthGetBalance ethGetBalance = web3j.ethGetBalance(credentials.getAddress(), DefaultBlockParameterName.LATEST).send();
            BigDecimal b = Convert.fromWei(ethGetBalance.getBalance().toString(), Convert.Unit.ETHER);
            return b;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 上链返回区块链hash
     * @param chain 上链对象
     * @return
     */
    public static String getChainHash(EthChain chain){
        return tran(JSONUtils.serialize(chain));
    }

    /**
     * 上链返回区块链hash
     * @param type 上链类型
     * @param arr  上链数据
     * @return
     */
    public static String getChainHash(EthChainType type,JSONArray arr){
        EthChain chain = new EthChain(type,arr);
        return getChainHash(chain);
    }

    /**
     * 上链返回区块链hash
     * @param jsonObject
     * @return
     */
    public static String getChainHash(JSONObject jsonObject){
        return tran(jsonObject.toJSONString());
    }

    /**
     * 上链返回区块链hash
     * @param array
     * @return
     */
    public static String getChainHash(JSONArray array){
        return tran(array.toJSONString());
    }

    /**
     * 上链
     * @param data json字符串
     * @return
     */
    public static String tran(String data){
        return tran(data,2);
    }

    private static final String GAS_ERROR = "intrinsic gas too low";

    /**
     * 上链
     * @param data json字符串
     * @return
     */
    public static String tran(String data,Integer size){
        EthGetTransactionCount ethGetTransactionCount = null;
        try {
            ethGetTransactionCount = web3j.ethGetTransactionCount(address, DefaultBlockParameterName.LATEST).sendAsync().get();
        } catch (Exception e) {
            LogKit.error("ethGetTransactionCount",e);
            throw new SystemException(-1,"网络异常,上链失败");
        }
        BigInteger nonce = ethGetTransactionCount.getTransactionCount();
        BigInteger value = Convert.toWei("0", Convert.Unit.ETHER).toBigInteger();

        EthGasPrice ethGasPrice = null;
        try {
            ethGasPrice = web3j.ethGasPrice().send();
        } catch (IOException e) {
            LogKit.error("ethGasPrice",e);
            throw new SystemException(-1,"网络异常,上链失败");
        }
        System.out.println(ethGasPrice.getGasPrice());
        RawTransaction rawTransaction = RawTransaction.createTransaction(
                nonce, ethGasPrice.getGasPrice(), GAS_LIMIT.multiply(new BigInteger(size.toString())), address, value,Numeric.toHexString(data.getBytes()));

        byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
        String hexValue = Numeric.toHexString(signedMessage);

        EthSendTransaction ethSendTransaction = null;
        try {
            ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send();
        } catch (IOException e) {
            LogKit.error("ethSendRawTransaction",e);
            throw new SystemException(-1,"网络异常,上链失败");
        }
        if(ethSendTransaction == null){
            throw new SystemException(-1,"网络异常,上链失败");
        }
        if (ethSendTransaction.getError() != null){
            if (ethSendTransaction.getError().getCode() == -32000){
                if (StringUtil.equals(GAS_ERROR,ethSendTransaction.getError().getMessage())){
                    return null;
                }
                throw new SystemException(-1,ethSendTransaction.getError().getMessage());
            }
        }
        String transactionHash = ethSendTransaction.getTransactionHash();
        if (StringUtil.isNotBlank(transactionHash)){
            return transactionHash;
        }
        return null;
    }

    /**
     * 获取区块高度和区块hash
     * @param hash
     * @return
     */
    public static Transaction getBlock(String hash){
        try {
            EthTransaction block = web3j.ethGetTransactionByHash(hash).send();
            Transaction transaction = block.getResult();
            if (transaction == null || BLOCK_HASH.equals(transaction.getBlockHash())){
                return null;
            }else{
                return transaction;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    public static void main(String[] args) {
//        Web3jUtil.getBalance();
        JSONObject data = new JSONObject();
        data.put("a","1");
        data.put("b","2");
        System.out.println(Numeric.cleanHexPrefix(data.toJSONString()));
        String transaction = Web3jUtil.tran(data.toJSONString());
        //System.out.println(transaction);
//        String hex = "0x7b2261223a2231222c2262223a2232227d";
//        byte[] b = Numeric.hexStringToByteArray(hex);
//        System.out.println(new String(b));
//        JSONObject jsonObject = JSON.parseObject(new String(b));
//        System.out.println(jsonObject.toJSONString());
//        Transaction transaction = getBlock("0x96b55c1d7b0d26229e733bbba785a6aceec6830b312c3fbf9cadde6e43a9adb1");
    }
}
